﻿using System;
using JetBrains.Annotations;
using MugenMvvmToolkit.Infrastructure;
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Interfaces.ViewModels;
using MugenMvvmToolkit.Interfaces.Views;
using MugenMvvmToolkit.Models;
using MugenMvvmToolkit.ViewModels;

namespace MugenMvvmToolkit
{
    /// <summary>
    ///     Represents the class that is used for unit tests.
    /// </summary>
    public abstract class UnitTestBase
    {
        #region Properties

        /// <summary>
        ///     Gets the current <see cref="IIocAdapter" />.
        /// </summary>
        protected IIocAdapter IocAdapter { get; private set; }

        /// <summary>
        /// Gets the <see cref="IViewModelProvider"/>.
        /// </summary>
        protected IViewModelProvider ViewModelProvider { get; private set; }

        /// <summary>
        ///     Gets or sets the view factory.
        /// </summary>
        protected Func<IViewModel, string, IView> GetViewDelegate { get; set; }

        /// <summary>
        /// Gets or sets the delegate that is used for initialize view.
        /// </summary>
        protected Func<IInitializationContext, bool> CanInitializeViewDelegate { get; set; }

        #endregion

        #region Methods

        /// <summary>
        ///     Initializes the current unit-test.
        /// </summary>
        protected void Initialize([NotNull]IIocAdapter iocAdapter, IInitializationStrategy strategy, IActivationPolicy defaultActivationPolicy = null)
        {
            Should.NotBeNull(iocAdapter, "iocAdapter");
            GlobalSettings.SetRootIocAdapterWithSettings(iocAdapter);
            if (defaultActivationPolicy != null)
                GlobalSettings.DefaultActivationPolicy = defaultActivationPolicy;
            if (strategy == null)
                strategy = new UnitTestInitializationStrategy();
            var unitTestInitializationStrategy = strategy as UnitTestInitializationStrategy;
            if (unitTestInitializationStrategy != null)
            {
                if (unitTestInitializationStrategy.GetViewDelegate == null)
                    unitTestInitializationStrategy.GetViewDelegate = GetViewInternal;
                if (unitTestInitializationStrategy.CanInitializeViewDelegate == null)
                    unitTestInitializationStrategy.CanInitializeViewDelegate = CanInitializeViewDelegateInternal;
            }
            strategy.Initialize(iocAdapter);
            IocAdapter = iocAdapter;
            ViewModelProvider = IocAdapter.Get<IViewModelProvider>();
        }

        /// <summary>
        ///     Creates an instance of the specified view model.
        /// </summary>
        /// <typeparam name="T">The type of view model.</typeparam>
        /// <param name="getViewModelFunc">The specified delegate to create view model.</param>
        /// <param name="policy">The specified <see cref="IActivationPolicy" />.</param>
        /// <param name="parentViewModel">The specified parent view model, if any.</param>
        /// <returns>
        ///     An instance of <see cref="IViewModel" />.
        /// </returns>
        protected T GetViewModel<T>([NotNull]GetViewModelDelegate<T> getViewModelFunc, IActivationPolicy policy = null,
            IViewModel parentViewModel = null)
            where T : IViewModel
        {
            return ViewModelProvider.GetViewModel(getViewModelFunc, policy, parentViewModel);
        }

        /// <summary>
        ///     Creates an instance of the specified view model.
        /// </summary>
        /// <param name="getViewModel">The specified delegate to create view model.</param>
        /// <param name="policy">The specified <see cref="IActivationPolicy" />.</param>
        /// <param name="parentViewModel">The specified parent view model, if any.</param>
        /// <returns>
        ///     An instance of <see cref="IViewModel" />.
        /// </returns>
        protected IViewModel GetViewModel([NotNull]GetViewModelDelegate<IViewModel> getViewModel,
            IActivationPolicy policy = null, IViewModel parentViewModel = null)
        {
            return ViewModelProvider.GetViewModel(getViewModel, policy, parentViewModel);
        }

        /// <summary>
        ///     Creates an instance of the specified view model.
        /// </summary>
        /// <typeparam name="T">The type of view model.</typeparam>
        /// <param name="policy">The specified <see cref="IActivationPolicy" />.</param>
        /// <param name="parentViewModel">The specified parent view model, if any.</param>
        /// <returns>
        ///     An instance of <see cref="IViewModel" />.
        /// </returns>
        protected T GetViewModel<T>(IActivationPolicy policy = null, IViewModel parentViewModel = null)
            where T : IViewModel
        {
            return ViewModelProvider.GetViewModel<T>(policy, parentViewModel);
        }

        /// <summary>
        ///     Creates an instance of the specified view model.
        /// </summary>
        /// <param name="viewModelType">The type of view model.</param>
        /// <param name="policy">The specified <see cref="IActivationPolicy" />.</param>
        /// <param name="parentViewModel">The specified parent view model, if any.</param>
        /// <returns>
        ///     An instance of <see cref="IViewModel" />.
        /// </returns>
        protected IViewModel GetViewModel([NotNull]Type viewModelType, IActivationPolicy policy = null,
            IViewModel parentViewModel = null)
        {
            return ViewModelProvider.GetViewModel(viewModelType, policy, parentViewModel);
        }

        /// <summary>
        ///     Gets an instance of <see cref="IView" /> for the specified view model.
        /// </summary>
        private IView GetViewInternal(IViewModel viewModel, string s)
        {
            Func<IViewModel, string, IView> @delegate = GetViewDelegate;
            Should.MethodBeSupported(@delegate != null, "IViewManager.GetView(IViewModel viewModel, string viewBindingName = null)");
            // ReSharper disable once PossibleNullReferenceException
            return @delegate(viewModel, s);
        }

        private bool CanInitializeViewDelegateInternal(IInitializationContext initializationContext)
        {
            Func<IInitializationContext, bool> viewDelegate = CanInitializeViewDelegate;
            if (viewDelegate == null)
                return false;
            return viewDelegate(initializationContext);
        }

        #endregion
    }
}